﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Algorithms;
using Infrastructure;
using Infrastructure.DataTypes;
using System.Diagnostics;


namespace Algorithms
{


    /// <summary>
    /// Pearson Correlation  prediction algorithm
    /// </summary>
    /// <typeparam name="RatingType"></typeparam>
    public class PearsonPrediction<RatingType> : PredictionAlgorithm<RatingType> where RatingType : ICountable, new()
    {

        /// <summary>
        /// predict rating user will give for an item
        /// </summary>
        /// <param name="user"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        public override double PredictRating(User<RatingType> user, Item<RatingType> item)
        {
            double predictedRating = 0;
            double numerator1 = 0;
            //double numerator2 = 0;
            double denominator = 0;
            double correlation = 0;

            foreach (User<RatingType> user2 in item.Scores.Keys)
            {
                if (!user.Equals(user2))
                {
                    correlation = PearsonCorrelation(user, user2);
                    if (correlation >= 0)
                        numerator1 += (correlation * user2.Votes[item]);
                    //else
                        //numerator2 += (MyLib.CalculateMax(user2.Votes.Values) + MyLib.CalculateMin(user2.Votes.Values) - user2.Votes[item]) * (-correlation);
                    denominator += Math.Abs(correlation);
                }
                
            }
            predictedRating = numerator1/denominator;

            return predictedRating;


        }
        public override string Name
        {
            get { return "Pearson"; }
        }

        public double PearsonCorrelation(User<RatingType> u1, User<RatingType> u2)
        {
            double W = 0;             //answer
            double numerator = 0;       //whole numerator
            double numerator1 = 0;      //left numerator
            double numerator2 = 0;      //right numerator
            double denominator = 0;     //whole denominator
            double denominator1 = 0;    //left denominator
            double denominator2 = 0;    //right denominator

            foreach (Item<RatingType> item in u1.Votes.Keys)
            {
                if (u2.Votes.ContainsKey(item))
                {
                    numerator1 = u1.Votes[item].Value - MyLib.CalculateMean(u1.Votes.Values<RatingType>);
                    numerator2 = u2.Votes[item].Value - MyLib.CalculateMean(u1.Votes.Values.ToArray<int>);
                    numerator += numerator1 * numerator2;
                    denominator1 += Math.Pow(numerator1, 2);
                    denominator2 += Math.Pow(numerator2, 2);
                }
            }

            denominator = Math.Sqrt(denominator1) * Math.Sqrt(denominator2);
            if (denominator == 0)   //to prevent division by zero error
                return 0;

            W = numerator/denominator;
            return W;

        }

        

    }
}
